package small.common.core.io.util;

import small.common.core.constants.CommonConstants;
import small.common.core.system.util.JvmUtil;
import small.common.core.util.StringUtil;

import java.io.*;
import java.nio.channels.FileChannel;
import java.util.zip.GZIPInputStream;

/**
 * io工具类
 *
 * @Author zhoujin
 * @Date 2022/11/21 20:10
 */
public class FileIoUtil {

    private static final String NO_SPACE_CN = "设备上没有空间";

    private static final String NO_SPACE_EN = "No space left on device";

    private static final String DISK_QUATA_CN = "超出磁盘限额";

    private static final String DISK_QUATA_EN = "Disk quota exceeded";

    //----------------------------------------------GZIP

    /**
     * 压缩文件成GZIP格式
     *
     * @param raw 压缩字节数组
     * @return
     * @throws Exception
     */
    public static byte[] tryDecompress(byte[] raw) throws Exception {
        if (!isGzipStream(raw)) {
            return raw;
        }
        GZIPInputStream gis = null;
        ByteArrayOutputStream out = null;

        try {
            gis = new GZIPInputStream(new ByteArrayInputStream(raw));
            out = new ByteArrayOutputStream();
            FileIoUtil.copy(gis, out);
            return out.toByteArray();
        } finally {
            closeQuietly(out);
            closeQuietly(gis);
        }
    }


    /**
     * 将GZIP从流中解压缩
     *
     * @param raw
     * @return
     * @throws IOException
     */
    public static byte[] tryDecompress(InputStream raw) throws IOException {
        GZIPInputStream gis = null;
        ByteArrayOutputStream out = null;
        try {
            gis = new GZIPInputStream(raw);
            out = new ByteArrayOutputStream();
            copy(gis, out);
            return out.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closeQuietly(out);
            closeQuietly(gis);
        }

        return null;
    }

    /**
     * 判断是否是GZIP格式
     *
     * @param bytes
     * @return
     */
    public static boolean isGzipStream(byte[] bytes) {

        int minByteArraySize = 2;
        if (bytes == null || bytes.length < minByteArraySize) {
            return false;
        }

        return GZIPInputStream.GZIP_MAGIC == ((bytes[1] << 8 | bytes[0]) & 0xFFFF);
    }

    //----------------------------------------------读取文件

    /**
     * 读取文件
     *
     * @param file 目标文件
     * @return
     * @throws IOException
     */
    public static String readFile(File file) throws IOException {
        if (!file.exists() || !file.isFile() || !file.canRead()) {
            return null;
        }

        //使用多实例读取文件
        if (JvmUtil.isMultiInstance()) {
            return ConcurrentDiskUtil.getFileContent(file, CommonConstants.ENCODE);
        } else {
            InputStream is = null;
            try {
                //读取资源 转化为字符串
                is = new FileInputStream(file);
                return FileIoUtil.toString(is, CommonConstants.ENCODE);
            } finally {
                try {
                    if (null != is) {
                        is.close();
                    }
                } catch (IOException ignored) {
                }
            }
        }
    }

    public static String toString(InputStream input, String encoding) throws IOException {
        if (input == null) {
            return StringUtil.EMPTY;
        }
        return (null == encoding) ? toString(new InputStreamReader(input, CommonConstants.ENCODE))
                : toString(new InputStreamReader(input, encoding));
    }


    /**
     * 从读取器读取字符串
     *
     * @param reader 读取器
     * @return string
     * @throws IOException io exception
     */
    public static String toString(Reader reader) throws IOException {
        CharArrayWriter sw = new CharArrayWriter();
        copy(reader, sw);
        return sw.toString();
    }

    /**
     * 复制数据源
     *
     * @param input  输入资源
     * @param output 输出资源
     * @return
     * @throws IOException
     */
    public static long copy(Reader input, Writer output) throws IOException {
        char[] buffer = new char[1 << 12];
        long count = 0;
        for (int n = 0; (n = input.read(buffer)) >= 0; ) {
            output.write(buffer, 0, n);
            count += n;
        }
        return count;
    }


    public static long copy(InputStream input, OutputStream output) throws IOException {
        byte[] buffer = new byte[1024];
        int bytesRead;
        int totalBytes = 0;
        while ((bytesRead = input.read(buffer)) != -1) {
            output.write(buffer, 0, bytesRead);

            totalBytes += bytesRead;
        }

        return totalBytes;
    }
    //----------------------------------------写入文件

    public static void writeBytesToFile(File file, byte[] data,Boolean append) throws IOException {
        OutputStream os = null;
        try {
            os = openOutputStream(file,append);
            os.write(data);
            os.flush();
        }catch (IOException ioe) {
            if (ioe.getMessage() != null) {
                String errMsg = ioe.getMessage();
                if (NO_SPACE_CN.equals(errMsg) || NO_SPACE_EN.equals(errMsg) || errMsg.contains(DISK_QUATA_CN) || errMsg
                        .contains(DISK_QUATA_EN)) {
                    System.exit(0);
                }
            }
            throw ioe;
        }finally {
            closeQuietly(os);
        }
    }

    /**
     * 将字符串写入目标文件(覆盖)
     *
     * @param file     目标文件
     * @param data     内容
     * @param encoding 编码方式
     * @param append 是否
     * @throws IOException
     */
    public static void writeStringToFile(File file, String data, String encoding,boolean append) throws IOException {
        writeBytesToFile(file,data.getBytes(encoding),append);
    }

    //----------------------------------------删除文件

    /**
     * 删除目标文件或目录
     *
     * @param fileOrDir 文件或目录
     * @throws IOException
     */
    public static void delete(File fileOrDir) throws IOException {
        if (fileOrDir == null) {
            return;
        }

        if (fileOrDir.isDirectory()) {
            cleanDirectory(fileOrDir);
        } else {
            if (fileOrDir.exists()) {
                boolean isDeleteOk = fileOrDir.delete();
                if (!isDeleteOk) {
                    throw new IOException("delete fail");
                }
            }
        }
    }

    /**
     * 删除目录
     *
     * @param directory 目录
     */
    public static void cleanDirectory(File directory) throws IOException {
        if (!directory.exists()) {
            String message = directory + " does not exist";
            throw new IllegalArgumentException(message);
        }

        if (!directory.isDirectory()) {
            String message = directory + " is not a directory";
            throw new IllegalArgumentException(message);
        }

        File[] files = directory.listFiles();
        // null if security restricted
        if (files == null) {
            throw new IOException("Failed to list contents of " + directory);
        }

        IOException exception = null;
        for (File file : files) {
            try {
                delete(file);
            } catch (IOException ioe) {
                exception = ioe;
            }
        }

        if (null != exception) {
            throw exception;
        }
    }

    //--------------------------------复制

    /**
     * 复制文件到目标路径
     *
     * @param source 源文件路径
     * @param target 目标文件路径
     * @throws IOException
     */
    public static void copyFile(String source, String target) throws IOException {
        File sf = new File(source);
        if (!sf.exists()) {
            throw new IllegalArgumentException("source file does not exist.");
        }
        File tf = new File(target);
        if (!tf.getParentFile().mkdirs()) {
            throw new RuntimeException("failed to create parent directory.");
        }
        if (!tf.exists() && !tf.createNewFile()) {
            throw new RuntimeException("failed to create target file.");
        }

        FileChannel sc = null;
        FileChannel tc = null;
        try {
            tc = new FileOutputStream(tf).getChannel();
            sc = new FileInputStream(sf).getChannel();
            sc.transferTo(0, sc.size(), tc);
        } finally {
            closeQuietly(sc);
            closeQuietly(tc);
        }
    }

    /**
     * 关闭资源
     *
     * @param closeable 可关闭资源
     */
    public static void closeQuietly(Closeable closeable) {
        try {
            if (closeable != null) {
                closeable.close();
            }
        } catch (IOException ignored) {
        }
    }

    public static FileOutputStream openOutputStream(File file, boolean append) throws IOException{
        if (file.exists()) {
            if (file.isDirectory()) {
                throw new IOException("File '" + file + "' exists but is a directory");
            }

            if (!file.canWrite()) {
                throw new IOException("File '" + file + "' cannot be written to");
            }
        } else {
            File parent = file.getParentFile();
            if (parent != null && !parent.mkdirs() && !parent.isDirectory()) {
                throw new IOException("Directory '" + parent + "' could not be created");
            }
        }
        return new FileOutputStream(file, append);
    }


}
